package com.android.task4;

public class MercatorProjection extends Projection {
	private static final int EARTH_RADIUS = 6378137;
	private static final double EARTH_CIRCUMFERENCE = 2 * Math.PI
			* EARTH_RADIUS;
	private static final double EARTH_HALF_CIRCUMFERENCE = EARTH_CIRCUMFERENCE / 2;

	public static final double MIN_LATITUDE = Math.toDegrees(Math.atan(Math
			.sinh(-Math.PI)));
	public static final double MAX_LATITUDE = Math.toDegrees(Math.atan(Math
			.sinh(Math.PI)));
	public static final double MIN_LONGITUDE = -180;
	public static final double MAX_LONGITUDE = 180;

	@Override
	public int latToY(final double lat, final int zoomLevel) {
		double latc = Clip(lat, MIN_LATITUDE, MAX_LATITUDE);
		double sinLat = Math.sin(Math.toRadians(latc));
		double metersY = EARTH_RADIUS / 2
				* Math.log((1 + sinLat) / (1 - sinLat));
		double coordY = Math.round((EARTH_HALF_CIRCUMFERENCE - metersY)
				/ earthArc(zoomLevel));
		return (int) Clip(coordY, 0, (1 << zoomLevel) - 1);
	}

	@Override
	public int lonToX(final double lon, final int zoomLevel) {
		double lonc = Clip(lon, MIN_LONGITUDE, MAX_LONGITUDE);
		double metersX = EARTH_RADIUS * Math.toRadians(lonc);
		double coordX = Math.round((EARTH_HALF_CIRCUMFERENCE + metersX)
				/ earthArc(zoomLevel));
		return (int) Clip(coordX, 0, (1 << zoomLevel) - 1);
	}

	@Override
	public double yToLat(final int y, final int zoomLevel) {
		double metersY = EARTH_HALF_CIRCUMFERENCE
				- Clip(y, 0, (1 << zoomLevel) - 1) * earthArc(zoomLevel);
		double exp = Math.exp(metersY / (EARTH_RADIUS / 2));
		return Math.toDegrees(Math.asin((exp - 1) / (exp + 1)));
	}

	@Override
	public double xToLon(final int x, final int zoomLevel) {
		double metersX = Clip(x, 0, (1 << zoomLevel) - 1) * earthArc(zoomLevel)
				- EARTH_HALF_CIRCUMFERENCE;
		return Math.toDegrees(metersX / EARTH_RADIUS);
	}

	public double earthArc(final int zoomLevel) {
		return EARTH_CIRCUMFERENCE / (1 << zoomLevel);
	}
}

